#include<iostream>
#include<string>

class Token{
private:
    enum {INT, CHAR, DBL, STR} tok;
    union {
        int a;
        char b;
        double c;
        std::string s;
    };

    void copyUnion(const Token&);

public:
    Token(): tok(INT), a(0) {}
    Token(const Token& t): tok(t.tok){ copyUnion(t);}
    Token &operator=(const Token &);
    ~Token(){ if(tok == STR) s.std::string::~string();}

    Token &operator=(const std::string&);
    Token &operator=(int);
    Token &operator=(char);
    Token &operator=(double);

    friend std::ostream& operator<<(std::ostream &os, const Token& t);

};

std::ostream& operator<<(std::ostream &os, const Token& t){
    std::cout<< "Type: " << t.tok;
    switch(t.tok){
        case Token::INT: std::cout << " Value: " << t.a; break;
        case Token::CHAR: std::cout << " Value: " << t.b; break;
        case Token::DBL: std::cout << " Value: " << t.c; break;
        case Token::STR: std::cout << " Value: " << t.s; break;
    }
    return os;
}

Token &Token::operator=(int i){
    if(tok == STR) s.std::string::~string();
    a = i;
    tok = INT;
    return *this;
}

Token &Token::operator=(char i){
    if(tok == STR) s.std::string::~string();
    b = i;
    tok = CHAR;
    return *this;
}

Token &Token::operator=(double i){
    if(tok == STR) s.std::string::~string();
    c = i;
    tok = DBL;
    return *this;
}

Token &Token::operator=(const std::string& i){
    if(tok == STR) s = i;
    else new(&s) std::string(i);
    tok = STR;
    return *this;
}

void Token::copyUnion(const Token& t){
    switch(t.tok){
        case Token::INT: a = t.a; break;
        case Token::CHAR: b = t.b; break;
        case Token::DBL: c = t.c; break;
        case Token::STR: new(&s) std::string(t.s); break;
    }
}

Token &Token::operator=(const Token& t){
    if (tok == STR && t.tok != STR) s.std::string::~string();
    if (tok == STR && t.tok == STR) s = t.s;
    else copyUnion(t);
    tok = t.tok;
    return *this;
}

union test {
    int a;
    char b;
    double c;
};

int main(){
    Token token;
    token = 1;
    token = 'a';
    token = 3.5;
    token = "hello";

    Token tmp_1;
    tmp_1 = "world";


    Token tmp_2(tmp_1);

    tmp_2 = 3.3;
    std::cout<< tmp_2 <<std::endl;
    tmp_1 = tmp_2;
    std::cout<< tmp_1 <<std::endl;
    token = tmp_1;
    std::cout<< token << std::endl;

    test t;
    std::cout<< sizeof(t) << std::endl;
    return 0;
}